﻿using System;
using System.Collections.Generic;
using System.Text;
using LC;

namespace Scan
{
    /// <summary>
    /// 提供网络访问失败（主要是手工下载）时，需要重试或重连网络的操作
    /// </summary>
    public class RetryManager
    {
        public const int NETFAIL = -2;
        public const int NOCONNECTSERVER = -3;
        public const int CONNECTTIMEOUT = -4;
        private static NLog.Logger logger = NLog.LogManager.GetLogger("RetryManager");
        private static object _lock = new object();
        private static bool isExecuting = false;
        public delegate string RetryFuncDelegate(params object[] paramters);

        public class RetryException : System.Exception
        {
            public int ErrorCode
            {
                get;
                set;
            }

            public RetryException(int errorcode,string msg) : base(msg) 
            {
                this.ErrorCode = errorcode;
            }
        }

        /// <summary>
        /// 重试次数
        /// </summary>
        private int retryTimes;

        private IntPtr hEvent;
        /// <summary>
        /// 连接网络的结果，true成功，false失败
        /// </summary>
        private bool isNetworkConnected;

        /// <summary>
        /// 要执行的方法
        /// </summary>
        private RetryFuncDelegate funcDelegate;

        /// <summary>
        /// 要执行的方法的参数值
        /// </summary>
        private object[] paramters;

        /// <summary>
        /// 实例化一个RetryManager对象
        /// </summary>
        /// <param name="retryTimes">重试次数</param>
        /// <param name="funcDelegate">要执行的方法</param>
        /// <param name="paramters">要执行的方法的参数值</param>
        public RetryManager(int retryTimes, RetryFuncDelegate funcDelegate, params object[] paramters)
        {
            this.retryTimes = retryTimes;
            isNetworkConnected = false;
            this.funcDelegate = funcDelegate;
            this.paramters = paramters;
            hEvent = Win32.CreateEvent(IntPtr.Zero, true, false, null);
        }

        ~RetryManager()
        {
            Win32.CloseHandle(hEvent);
        }

        /// <summary>
        /// 获取当前是否在执行网络操作的指示值
        /// </summary>
        public static bool IsExecuting
        {
            get
            {
                lock (_lock)
                {
                    return isExecuting;
                }
            }
        }

        /// <summary>
        /// 执行网络访问，需要通过在独立于UI的线程中调用该方法
        /// </summary>
        /// <returns>返回自定义消息</returns>
        public string Execute()
        {
            try
            {
                lock (_lock)
                {
                    isExecuting = true;
                }
                return DoExecute();
            }
            catch
            {
                throw;
            }
            finally
            {
                lock (_lock)
                {
                    isExecuting = false;
                }
            }
        }

        /// <summary>
        /// 执行网络访问，需要通过在独立于UI的线程中调用该方法
        /// </summary>
        /// <returns>返回自定义消息</returns>
        private string DoExecute()
        {
            if (!Device.CheckNetworkStat())
            {
                if (!ConnectNetwork(false))
                {
                    if (!ConnectNetwork(true))
                    {
                        throw new RetryException(NETFAIL, "无法连接网络！");
                    }
                }
            }

            int tryCount = 0;
            while (true) // Will retry retryTimes
            {
                try
                {
                    // Execute
                    return funcDelegate(paramters);
                }
                catch (System.ServiceModel.CommunicationException cex)
                {
                    logger.ErrorException(String.Format("CommunicationException[{0}/{1}]", tryCount, retryTimes), cex);
                    if (++tryCount > retryTimes)        // 重试
                    {
                        throw new RetryException(NOCONNECTSERVER, "无法连接服务器！");
                    }
                    if (tryCount > retryTimes - 1)      // 重试了还是网络失败，则重新连接网络后再重试最后一次
                    {
                        if (!ConnectNetwork(true))
                        {
                            throw new RetryException(NETFAIL, "无法连接网络！");
                        }
                    }
                }
                catch (System.Net.WebException wex)
                {
                    logger.ErrorException(String.Format("WebException[{0}/{1}]", tryCount, retryTimes), wex);
                    if (++tryCount > retryTimes)        // 重试
                    {
                        throw new RetryException(NOCONNECTSERVER, "无法连接服务器！");
                    }
                    if (tryCount > retryTimes - 1)      // 重试了还是网络失败，则重新连接网络后再重试最后一次
                    {
                        if (!ConnectNetwork(true))
                        {
                            throw new RetryException(NETFAIL, "无法连接网络！");
                        }
                    }
                }
                catch (System.TimeoutException tex)
                {
                    logger.ErrorException(String.Format("TimeoutException[{0}/{1}]", tryCount, retryTimes), tex);
                    if (++tryCount > retryTimes)        // 重试
                    {
                        throw new RetryException(CONNECTTIMEOUT, "连接服务器超时！");
                    }
                    if (tryCount > retryTimes - 1)      // 重试了还是网络失败，则重新连接网络后再重试最后一次
                    {
                        if (!ConnectNetwork(true))
                        {
                            throw new RetryException(NETFAIL, "无法连接网络！");
                        }
                    }
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// 连接网络并等待返回
        /// </summary>
        /// <param name="reconnect">是否重连</param>
        /// <returns>连接状态，true成功，false失败</returns>
        private bool ConnectNetwork(bool reconnect)
        {
            OnConnectStart(reconnect);

            // Wait for network connection
            Win32.WaitForSingleObject(hEvent, Win32.INFINITE);

            return isNetworkConnected;
        }

        /// <summary>
        /// 开始连接网络
        /// </summary>
        /// <param name="reconnect">是否重连</param>
        private void OnConnectStart(bool reconnect)
        {
            Win32.EventModify(hEvent, Win32.EVENT_RESET);
            GlobalCache.Instance.NetworkMgr.ConnectNotify += new ConnectNotifyEventHandler(OnConnectNotify);
            GlobalCache.Instance.NetworkMgr.Connect(NetworkManager.ConnectType);
        }

        private void OnConnectNotify(object sender, ConnectNotifyEventArgs e)
        {
            if (e.ConnectResult != ConnState.Connecting)
            {
                GlobalCache.Instance.NetworkMgr.ConnectNotify -= new ConnectNotifyEventHandler(OnConnectNotify);

                isNetworkConnected = e.ConnectResult == ConnState.ConnectSuccess;

                Win32.EventModify(hEvent, Win32.EVENT_SET);
            }
        }
    }
}
